import { Injectable, Logger } from '@nestjs/common';
import { Script, createContext } from 'vm';

@Injectable()
export class BusService {
  constructor() {
    this.timeout = 3000;
    this.maxIterations = 1000;
  }

  private readonly logger = new Logger(BusService.name);
  private readonly timeout: number;
  private readonly maxIterations: number;

  private readonly validatorRegexMap = {
    'Bus.Random': {
      regex: /^Bus\.Random\[\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\]$/,
    },
    'Bus.BetweenRandom': {
      regex:
        /^Bus\.BetweenRandom\[\s*(-?\d+(?:\.\d+)?)\s*(?:,\s*(-?\d+(?:\.\d+)?)\s*)*\]$/,
    },
    'Bus.Sum': {
      regex:
        /^Bus\.Sum\[T1,\s*(\d+),\s*(second|minute|hour|day|month|year),\s*([A-Za-z0-9]+)\]$/,
    },
    'Bus.Max': {
      regex:
        /^Bus\.Sum\[T1,\s*(\d+),\s*(second|minute|hour|day|month|year),\s*([A-Za-z0-9]+)\]$/,
    },
    'Bus.Min': {
      regex:
        /^Bus\.Sum\[T1,\s*(\d+),\s*(second|minute|hour|day|month|year),\s*([A-Za-z0-9]+)\]$/,
    },
    'Bus.AVG': {
      regex:
        /^Bus\.Sum\[T1,\s*(\d+),\s*(second|minute|hour|day|month|year),\s*([A-Za-z0-9]+)\]$/,
    },
  };

  private validator(expr: string): boolean | number {
    const type = expr.split('[')[0];
    const _type = this.validatorRegexMap[type];
    if (_type) {
      return _type.regex.test(expr) ? true : 0;
    } else {
      return false;
    }
  }

  private replaceExpression(expression: string): string {
    return expression
      .replaceAll('[', '(')
      .replaceAll(']', ')')
      .replaceAll('T1', '"T1"')
      .replace(/\b(second|minute|hour|day|month|year)\b/g, '"$1"');
  }

  // 计算两个值之间的随机数
  private safeRandom(min: number, max: number): string {
    return (Math.random() * (max - min) + min).toFixed(2);
  }

  // 随机获取数组列表中一个
  private safeBetweenRandom(...args: number[]): string {
    if (args.length === 0) return '0';
    return args[Math.floor(Math.random() * args.length)].toFixed(2);
  }

  // 计算并求出一段时间指定业务单位的指定标签的累计值
  private safeSum(...args: any[]): any {
    // TODO: 实现具体逻辑或注入相关服务
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的平均值
  private safeAvg(...args: any[]): any {
    // TODO: 实现具体逻辑或注入相关服务
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的最大值
  private safeMax(...args: any[]): any {
    // TODO: 实现具体逻辑或注入相关服务
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的最小值
  private safeMin(...args: any[]): any {
    // TODO: 实现具体逻辑或注入相关服务
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的第一条值
  private safeFir(...args: any[]): number {
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的最后一条值
  private safeLas(...args: any[]): number {
    return 0;
  }

  // 计算并求出一段时间指定业务单位的指定标签的个数
  private safeCou(...args: any[]): number {
    return 0;
  }

  calculate(expression: string): any {
    const isValidator = this.validator(expression);
    if (isValidator === 0) {
      return 0;
    }

    const _expression = this.replaceExpression(expression);

    try {
      // 创建隔离环境
      const context = createContext({
        Bus: {
          Random: this.safeRandom.bind(this),
          BetweenRandom: this.safeBetweenRandom.bind(this),
          Sum: this.safeSum.bind(this),
          Max: this.safeMax.bind(this),
          Min: this.safeMin.bind(this),
          Avg: this.safeAvg.bind(this),
          Fir: this.safeFir.bind(this),
          Las: this.safeLas.bind(this),
          Cou: this.safeCou.bind(this),
        },
        // 禁用危险对象
        global: undefined,
        process: undefined,
        Buffer: undefined,
        require: undefined,
        module: undefined,
        exports: undefined,
        __dirname: undefined,
        __filename: undefined,
      });

      const script = new Script(_expression);
      return script.runInContext(context, {
        timeout: this.timeout,
        displayErrors: true,
        breakOnSigint: true,
      });
    } catch (error) {
      this.logger.error(`VM计算错误: ${error.message}`);
      return null;
    }
  }

  executeJsConvert(code: string, data: any): any {
    if (!code) return data;
    const _code = `(${code})`;

    try {
      const func = new Function(`return ${_code}`)();
      return JSON.parse(func(JSON.stringify(data)));
    } catch (error) {
      return {
        code: 0,
        error,
      };
    }
  }
}